
/*
*案例代码1：
* 客户端连接服务器，服务器创建线程，处理客户端的请求
* 信号方式通知服务器关闭
* 引入客户端描述的结构体
* 通过数据库根据客户端Ip查询数据库获取客户端信息
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h> // 文件IO

#include <sys/socket.h> // socket
#include <sys/types.h>
#include <arpa/inet.h>
#include <netinet/in.h>

#include <pthread.h>  // 线程
#include <mysql/mysql.h> // mysql

int run_flag = 1;

void handler(int sig)
{
	if (sig == 10) {
		run_flag = 0;
	}
}
// 抽象客户端
typedef struct client {
	int sock_fd;
	int port;
	char ip[16];
	char name[32];
}Client;

void * deal_client(void *arg);
int mysql_select(const char *db, const char *ip, char *name);

// 主线程的main入口函数
int main(int argc, const char* argv[])
{
	//char name[32];
	//mysql_select("park", "192.168.1.102", name);
#if 1
	if (argc != 3) {
		printf("命令格式： cmd ip_addr port");
		return 0;
	}
	signal(10, handler);
	int ret;
	// 创建socket
	int sock_fd;
	sock_fd = socket(AF_INET, SOCK_STREAM, 0);
	if (sock_fd == -1) {
		perror("socket");
		return -1;
	}
	
	// 绑定自己的IP
	struct sockaddr_in local_addr;
	local_addr.sin_family = AF_INET; // 协议族
	local_addr.sin_port = htons(atoi(argv[2])); // 指定服务器的端口号
	local_addr.sin_addr.s_addr = inet_addr(argv[1]); // 指定服务器IP地址
	
	ret = bind(sock_fd, (struct sockaddr *)&local_addr, sizeof(local_addr));
	if (ret == -1) {
		perror("bind");
		return -1;
	}
	printf("Server listen at : %s : %d\n", argv[1], atoi(argv[2]));
	// 服务器listen客户端
	listen(sock_fd, 10);
	
	// 服务器接收连接
	struct sockaddr_in client_addr;
	bzero(&client_addr, sizeof(client_addr));
	socklen_t len = sizeof(client_addr);
	int client_sock_fd;
	
	printf("server running ...\n");
	
	while (run_flag == 1) {
		client_sock_fd = accept(sock_fd, (struct sockaddr *)&client_addr, &len); // 取出一个客户端连接，当没有连接时则阻塞
		if (client_sock_fd == -1) {
			perror("accept");
			return -1;
		}
		
		// 创建线程
		pthread_t tid;
		Client *client = malloc(sizeof(Client)); // 创建客户端对象
		if (client == NULL) { // 代码健壮性
			close(client_sock_fd);
			continue;
		}
		bzero(client, sizeof(Client));
		client->sock_fd = client_sock_fd; // 填充sock fd
		client->port = ntohs(client_addr.sin_port); // 填端口
		strcpy(client->ip, inet_ntoa(client_addr.sin_addr)); // 填IP
		printf("client %s connect server succeful\n", client->ip);
		
		if (0 != pthread_create(&tid, NULL, deal_client, (void *)client)) {// 创建线程
			printf("thread create fail\n");
			continue;
		} 
		pthread_detach(tid); // 分离线程,当线程退出时自动回收其资源
	}
	// 关闭连接
	close(sock_fd);
#endif
	
    return 0;
}

// 处理客户端的线程入口
void * deal_client(void *arg)
{
	Client * client = (Client *)arg;
	int ret;
	char ip_buf[16] = ""; // 需要传进来
	// 处理一个客户端连接
	char req_buf[128];
	
	// 查表，获取客户端的详细信息
	mysql_select("park", client->ip, client->name);
	while (1) {
		// 从客户端读取请求数据
		memset(req_buf, 0, sizeof(req_buf));
		ret = read(client->sock_fd, req_buf, sizeof(req_buf));
		if (ret == -1) {
			perror("read");
			break;
		}
		if (ret == 0) {
			printf("[%s] 异常退出\n", ip_buf);
			break;
		}
		printf("server recv [%s]: %s\n", client->name, req_buf);
		
		// 客户端的请求处理
		ret = write(client->sock_fd, "OK",strlen("OK"));
		if (ret == -1) {
			perror("write");
			break;
		}
		// 检查退出条件
		if (0 == strncmp(req_buf, "quit", strlen("quit"))) {
			
			printf("[%s] disconnect\n", client->name);
			break;
		}
	}
	close(client->sock_fd); // 关闭与客户端的连接
	free(client); // 释放client对象
	pthread_exit(NULL);
}
/*
从表中查询数据
*/
int mysql_select(const char *db, const char *ip, char *name)
{
	// step1 初始化连接句柄
	MYSQL * conn;
	conn = mysql_init(NULL);
	if (conn == NULL) {
		printf("mysql init failed\n");
		return -1;
	}
	// step2 与mysql建立连接
	conn = mysql_real_connect(conn, "localhost", "root", "123", db, 0, NULL,0);
	if (conn == NULL) {
		printf("mysql connect failed\n");
		return -1;
	}

	// step3 执行sql语句
	int ret;
	//printf("请输入识别到的车牌号：");
	mysql_query(conn, "SET NAMES utf8"); // 设置中文字符集

	char sql_buf[128] = "";
	sprintf(sql_buf, "select * from user where ip_addr = '%s'", ip);
	ret = mysql_query(conn, sql_buf); // 执行sql语句
	if (ret != 0) {
		printf("mysql query failed\n");
	}
	MYSQL_RES *res;
	res = mysql_store_result(conn); // 存储查询结果
	if (res == NULL) {
		printf("存储结果失败\n");
	}
	
	unsigned long long rows;
	rows = mysql_num_rows(res); // 获取结果集的行数
	if (rows == 0) { // 该名字不在表中
		// step4 结果空间释放
		mysql_free_result(res);
		// step5 断开连接
		mysql_close(conn);
		return -1;
	}
	
	unsigned int count;
	count = mysql_field_count(conn);
	printf("count = %d\n", count);
	
	unsigned int fields;
	fields = mysql_num_fields(res);
	printf("fields : %d\n", fields);
	
	MYSQL_ROW row; // typedef char[][] MYSQL_ROW;	
	row = mysql_fetch_row(res); // 获取一行结果
	strcpy(name, row[1]);
	printf("select res: %s\n", row[1]);
	// step4 结果空间释放
	mysql_free_result(res);
	// step5 断开连接
	mysql_close(conn);
	return 0;
}

